export * from "./utils";
import { md5 } from "js-md5";

type cbk = (...params: any) => any;
type obj = { [k: string]: any; };
declare function parseInt(s: any, radix?: number): number;
export interface UploadOption extends Omit<UniApp.UploadFileOption, "url"> {
  type?: string;
  other?: any;
  fileName?: string;
}

const iyq = 1000,
  d = {
    api: "", get token() { return { token: uni.getStorageSync("token") as string | undefined }; },
    ct: iyq * 10,
    beforeRequest: null as any,
  },
  cache: { [k: string]: { t: number; d: any; c: { resolve: cbk; reject: cbk; }[]; }; } = {}
  ;

/** 当前没有缓存 返回tru,表示新增加了一个等待队列 */
export function addCache(md5: string, resolve: cbk, reject: cbk, timeout?: number) {
  const c = cache[md5], t = new Date().getTime() + (timeout && timeout > 0 ? timeout : iyq);
  if (c) {
    c.c.push({ resolve, reject });
    c.t = t;
    if (typeof c.d !== "undefined") { execCache(md5, c.d); }
    return false;
  }
  cache[md5] = { t, d: undefined, c: [{ resolve, reject }] };
  return true;
}
export function execCache(md5: string, data: any, isReject?: any, timeout?: number) {
  const c = cache[md5];
  if (isReject) { delete cache[md5]; }
  if (c) {
    c.d = data;
    const rc = c.c.splice(0);
    isReject
      ? rc.forEach(f => { try { f.reject(c.d); } catch (_) { } })
      : rc.forEach(f => { try { f.resolve(c.d); } catch (_) { } });
    c.t = new Date().getTime() + (timeout && timeout > 0 ? timeout : iyq);
  }
}
export function base64ToFile(base64: string, fileName = 'image.png') {
  const ab = uni.base64ToArrayBuffer(base64.split(',')[1]);
  const blob = new Blob([ab], { type: 'image/png' });
  const file = new File([blob], fileName, { type: 'image/png' });
  return file;
}
export function formatUrl(url?: string, baseUrl?: string) {
  if (typeof url == "string" && url) {
    if (url.indexOf("data:image") && url.indexOf("http")) {
      if (!baseUrl) {
        baseUrl = "";
      }
      if (!url.startsWith("/")) {
        url = "/" + url;
      }
      url = baseUrl.replace(/\/+$/g, "") + url;
    }
  }
  return url || baseUrl || "";
}
/** 将一个url路径格式化成完整的url; 前提是已经设置过baseUrl */
export function fUrl(p?: string) { return formatUrl(p, d.api); }
/** 安全的 JSON.parse */
export function strParse<T>(p: any, def?: T): T {
  try {
    return JSON.parse(p);
  } catch (error) {
    return typeof p == "string" ? (def ? def : {} as T) : p;
  }
}
/** 设置request的执行前缀
 * @param c 回调函数
 */
export function setRequestBefore(c: cbk) {
  d.beforeRequest = c;
}
/** 设置基础apiUrl */
export function setBaseUrl(url: string) { d.api = url; return d.api; }
/** 设置request的缓存时间,防止短时间内多次发送同一请求
 * @param t 缓存时间(单位:毫秒);在最后一次请求时间内,将直接返回上次请求结果;默认10000(10秒)
 */
export function setRequestCacheTime(t: number) { if (t > 0) { d.ct = parseInt(t); } }
/** 消息提示 */
export function msg(m: string, type?: UniApp.ShowToastOptions["icon"]): Promise<any>;
export function msg(opt: UniApp.ShowToastOptions): Promise<any>;
export function msg(opt: UniApp.ShowToastOptions | string, type?: UniApp.ShowToastOptions["icon"]) {
  if (typeof opt === "string") { opt = { title: opt }; }
  if (type) { opt.icon = type; }
  if (!opt.duration) { opt.duration = 3000; }
  if (opt.mask === undefined) { opt.mask = true; }
  // @ts-ignore
  if (!opt.title && opt.message) { opt.title = opt.message; }
  // @ts-ignore
  if (!opt.icon && opt.code) {
    // @ts-ignore
    if (/^[45][0-9]+/.test(`${opt.code}`)) {
      opt.icon = "error";
    }
  }

  return uni.showToast(opt);
}
/**具有缓存功能的简化reques; 必须在uni环境下使用
 * @param url 请求地址
 * @param data 请求数据
 * @param opt uni的配置参数
 * @param real 是否跳过beforeRequest的前置方法;(setRequestBefore设置了前置后)
 * @returns 请求结果
 */
export function request(url?: string, data?: any, opt?: Partial<UniApp.RequestOptions> | null, real?: any) {
  return new Promise<any>(async (resolve, reject) => {
    if (url) {
      if (!real && d.beforeRequest instanceof Function) {
        await d.beforeRequest();
      }
      if (!opt) { opt = {}; }
      opt.url = fUrl(url || opt.url);
      if (!opt.method) { opt.method = "POST"; }
      if (!opt.header) { opt.header = d.token; }
      if (data) { opt.data = data; }
      const s = opt.success, f = opt.fail, m = md5(opt.url + opt.method + JSON.stringify([opt.header, opt.data]));
      opt.success = (res) => { execCache(m, res, false, d.ct); };
      opt.fail = (res) => { execCache(m, res, true, d.ct); };
      if (addCache(m, (res) => {
        const d: any = res.data;
        if (d.message) {
          const icon = d.type || (d.data ? "success" : "error");
          msg(Object.assign(d, { icon, title: d.message }));
          if (!d.data) { return reject(d); }
        }
        resolve(d);
        s instanceof Function && s(d);
      }, (r) => {
        msg({ icon: "error", title: r.errMsg });
        reject(r);
        f instanceof Function && f(r);
      }, d.ct)) {
        uni.request(opt as any);
      }
    }
  });
}
try { // 注册拦截器,拦截request的响应,在这里进行响应处理
  uni.addInterceptor("request", {
    complete(r) {
      try {
        if (r?.header?.message) {
          const title = decodeURIComponent(r.header.message);
          const message = strParse(title, { title });
          msg(message);
        }
      } catch (_) {
      }
    }
  });
} catch (_) {
}

/** 上传文件 */
export function upload(name: string, file: File | string, other?: obj, type?: string): Promise<string>;
export function upload(opt: UploadOption): Promise<string>;
export function upload(options: UploadOption | string): Promise<string> {
  let opt: UploadOption = options as any;
  if (typeof options === "string") {
    const [fileName, file, other, type] = arguments as any as [string, File | string, any, string];
    if (!file) { throw new Error("参数错误"); }
    opt = { other, type, fileName };
    if (file instanceof File) { opt.files = [file]; } else if (/^data:/i.test(file)) {
      opt.files = [base64ToFile(file.split(",")[1], fileName)];
    } else {
      opt.filePath = file;
    }
  }
  if (!opt.name) { opt.name = "file"; }
  if (!opt.other) { opt.other = {}; }
  if (typeof opt.other !== "string") { opt.other = JSON.stringify(opt.other); }
  opt.formData = Object.assign({}, { type: opt.type || "", other: opt.other }, opt.formData);
  delete opt.type;
  delete opt.other;

  const c = opt.complete;
  return new Promise<string>((resolve, reject) => {
    uni.uploadFile(Object.assign(opt, {
      url: fUrl("upload"),
      header: Object.assign(d.token, { ...opt.header }),
      complete(res: any) {
        const r: any = strParse(res.data);
        r.url ? resolve(fUrl(r.url)) : reject(res);
        if (c instanceof Function) { c(r); }
      }
    }));
  });
}